library(tidyverse)
library(plotly)
load("wireless.rda")
wireless_feature = wireless[,3:7] %>%
  mutate(d_S1 = apply(wireless[,1:2], 1, function(x){sqrt(sum((x - as.numeric(AP[1,]))^2))})) %>%
  mutate(d_S2 = apply(wireless[,1:2], 1, function(x){sqrt(sum((x - as.numeric(AP[2,]))^2))})) %>%
  mutate(d_S3 = apply(wireless[,1:2], 1, function(x){sqrt(sum((x - as.numeric(AP[3,]))^2))})) %>%
  mutate(d_S4 = apply(wireless[,1:2], 1, function(x){sqrt(sum((x - as.numeric(AP[4,]))^2))})) %>%
  mutate(d_S5 = apply(wireless[,1:2], 1, function(x){sqrt(sum((x - as.numeric(AP[5,]))^2))}))
plot(x=wireless$x, y=wireless$y)
points(x=AP$x,y=AP$y, col="red", cex=1)

cutoff = 100
sample_size = 40
num_iter = 1000
k_s = numeric(num_iter)
r_s = numeric(num_iter)
for (i in 1:num_iter) {
  temp_indices = sample((1:254)[distances < cutoff], sample_size)
  temp_mod = lm(wireless_feature[temp_indices ,1]~distances[temp_indices])
  k_s[i] = temp_mod$coefficients[2]
  r_s[i] = summary(temp_mod)$adj.r.squared
}
mean(k_s)
[1] -0.421104
mean(r_s)
[1] 0.8751914
# using bagging to find the coefficients, using 40 points 
k_s = numeric(num_iter)
for (i in 1:num_iter) {
  temp_indices = sample((1:254)[distances2 < cutoff], sample_size)
  temp_mod = lm(wireless_feature[temp_indices ,2]~distances2[temp_indices])
  k_s[i] = temp_mod$coefficients[2]
}
mean(k_s)
[1] -0.4250205

##exploring using differentials 
##

sample_index = sample(1:nrow(wireless), 1)
sample_point = wireless[sample_index,]
sample_diff = data.frame(t(apply(wireless[-sample_index,], 1, function(x) x - as.numeric(wireless[sample_index,]))))
sample_diff_y = sample_diff[sample_diff$y == 0,]

mod = lm(x~.-y, data=sample_diff_y)
summary(mod)
basic_x = lm(x~.-y, data=wireless)
basic_y = lm(y~.-x, data=wireless)
#summary(basic_x)
#summary(basic_y)
avg_error = mean(sqrt((wireless$x - basic_x$fitted.values)^2 + (wireless$y - basic_y$fitted.values)^2))
avg_error
[1] 17.20438
plot(wireless$x, wireless$y)
points(basic_x$fitted.values, basic_y$fitted.values, col="red")
segments(wireless$x, wireless$y, basic_x$fitted.values, basic_y$fitted.values, col="blue")
n = nrow(wireless)
#train_percent = 0.6
#sample_indices = sample(1:nrow(wireless), train_percent*n)
knn_predictions = numeric(n)
pwdistances = as.matrix(dist(wireless[,3:7]))
for (i in 1:n) {
  knn_predictions[i] = (1:n)[-i][which.min(as.matrix(pdist::pdist(wireless[,(3:7)][i,], wireless[,(3:7)][-i,])))]
}
knn_x = wireless$x[knn_predictions]
knn_y = wireless$y[knn_predictions]
par(mfrow=c(1,2))
plot(density(sqrt((wireless$x - knn_x)^2 + (wireless$y - knn_y)^2)),
     main = "knn performance")

#plot(density(sqrt((wireless$x - basic_x$fitted.values)^2 + (wireless$y - basic_y$fitted.values)^2)),
#     main = "regression performance")
knn_errors = sqrt((wireless$x - knn_x)^2 + (wireless$y - knn_y)^2)
knn_avg_error = mean(knn_errors[knn_errors < 100])
knn_avg_error
[1] 10.97828
plot(wireless$x, wireless$y)
points(knn_x, knn_y, col="red")
arrows(wireless$x, wireless$y, knn_x, knn_y, col="blue", length = 0.1)
zero-length arrow is of indeterminate angle and so skippedzero-length arrow is of indeterminate angle and so skipped

knn_bad_loc = wireless[knn_errors > 20,] %>%
  mutate(error = knn_errors[knn_errors > 20]) %>%
  mutate(index = (1:254)[knn_errors > 20]) %>%
  dplyr::arrange(desc(error))
error_bar = 15
plot(wireless$x, wireless$y)
points(knn_x[knn_errors > error_bar], knn_y[knn_errors > error_bar], col="red")
arrows(wireless$x[knn_errors > error_bar], wireless$y[knn_errors > error_bar], knn_x[knn_errors > error_bar], knn_y[knn_errors > error_bar], col="blue", length = 0.1)

par(mfrow=c(1,2))
hist(knn_errors)
plot(density(knn_errors))
par(mfrow=c(1,2))
hist(wireless$x[knn_errors > error_bar])
plot(density(wireless$x[knn_errors > error_bar]))

#trying nearest neightbor + trigulation
point_index = 113
point = wireless[point_index,3:7]
# difference based on signal strength from different access point
point_d = pdist::pdist(point, wireless[-point_index,3:7])@dist
# real distance based on coordinates
point_rd = pdist::pdist(wireless[point_index,1:2], wireless[-point_index,1:2])@dist
signal_neighbor = (1:254)[-point_index][order(point_d)[1:3]]
real_neighbors = (1:254)[-point_index][order(point_rd)[1:3]]
hover_text = apply(wireless_feature[,1:5],1, function(x) paste(x,collapse = "|"))
p = plot_ly(wireless, x=~x, y=~y, name = "receivers", type="scatter", 
            mode="markers", text=paste(1:254, "<br>", hover_text)) %>% 
  add_trace(x=AP$x, y=AP$y, name = "wifi post", mode="markers", text=rownames(AP)) %>%
  add_trace(x=wireless$x[signal_neighbor], y=wireless$y[signal_neighbor], 
            name = "neighbors", mode="markers", 
            text=paste(signal_neighbor, "<br>", hover_text[signal_neighbor])) %>%
  add_trace(x=wireless$x[real_neighbors], y=wireless$y[real_neighbors], 
            name = "neighbors", mode="markers", 
            text=paste(real_neighbors, "<br>", hover_text[real_neighbors])) %>%
  add_trace(x=wireless$x[point_index], y=wireless$y[point_index], 
            name = "neighbors", mode="markers", 
            text=paste(point_index, "<br>", hover_text[point_index]))
p
#wireless[c(point_index, signal_neighbor),]
point_neighbors = data.frame(t(apply(wireless[c(signal_neighbor, real_neighbors),], 1, 
                                     function(x) as.numeric(x-wireless[point_index, ])))) %>%
  dplyr::rename(dx=X1,dy=X2,dS1=X3,dS2=X4,dS3=X5,dS4=X6,dS5=X7) %>%
  dplyr::mutate(real_distance=c(point_rd[order(point_d)[1:3]], point_rd[order(point_rd)[1:3]])) %>% 
  dplyr::mutate(signal_distance=c(point_d[order(point_d)[1:3]], point_d[order(point_rd)[1:3]])) %>% 
  dplyr::mutate(relationship=c(rep("signal_neighbor",3),rep("real_neighbor", 3))) %>%
  rev()
plot.table(point_neighbors)
Error in plot.table(point_neighbors) : 
  could not find function "plot.table"
point_index = 113
point = wireless[point_index,3:7]
point_d = pdist::pdist(point, wireless[-point_index,(3:7)])@dist
top_three = (1:254)[-point_index][order(point_d)[1:3]]
neighbors = numeric(5)
for (i in 1:5) {
  neighbors[i] = (1:n)[-point_index][which.min(as.matrix(pdist::pdist(wireless[,(3:7)[-i]][point_index,], wireless[,(3:7)[-i]][-point_index,])))]
}
neighbors
[1]  33 153   9  33   4
dist(wireless[neighbors,1:2])
           33      153        9     33.1
153  38.47077                           
9    54.00000 79.62412                  
33.1  0.00000 38.47077 54.00000         
4    30.30000 59.06683 23.70000 30.30000

5

point
hover_text = apply(wireless_feature[,1:5],1, function(x) paste(x,collapse = "|"))
p = plot_ly(wireless, x=~x, y=~y, name = "receivers", type="scatter", 
            mode="markers", text=paste(1:254, "<br>", hover_text)) %>% 
  add_trace(x=AP$x, y=AP$y, name = "wifi post", mode="markers", text=rownames(AP)) %>%
  add_trace(x=wireless$x[neighbors], y=wireless$y[neighbors], 
            name = "neighbors", mode="markers", 
            text=paste(neighbors, "<br>", hover_text[neighbors])) #%>%
  #add_trace(x=wireless$x[point_index], y=wireless$y[point_index], 
  #          name = "point", mode="markers", 
  #          text=paste(point_index, "<br>", hover_text[point_index]))
  
p
knn_predictions[3]
[1] 37
neighbors = numeric(5)
for (i in 1:5) {
  neighbors[i] = (1:n)[-point_index][which.min(as.matrix(pdist::pdist(1/wireless[,(3:7)[-i]][point_index,], 1/wireless[,(3:7)[-i]][-point_index,])))]
}
neighbors

after analyzing the error, I find that point 243’s signal for AP3 is completely bad comparing to its neighbors, lets check other access points.

Some points, they only f* up on signal from an access point.

set.seed(12345)
sample_indices = sample(1:254, 50)
plot(wireless$x[sample_indices], wireless$y[sample_indices], ylim=c(0, 145), xlim=c(10, 235))
points(knn_x[sample_indices], knn_y[sample_indices], col="yellow")
points((knn_x-df_x)[sample_indices], (knn_y-df_y)[sample_indices], col="red")

segments(wireless$x[sample_indices], wireless$y[sample_indices], knn_x[sample_indices], knn_y[sample_indices], col="blue")
segments(knn_x[sample_indices], knn_y[sample_indices], (knn_x-df_x)[sample_indices], (knn_y-df_y)[sample_indices], col="green")
#segments(wireless$x, wireless$y, knn_x+df_x, knn_y+ df_y, col="green")
par(mfrow=c(1,2))
plot(wireless_feature$S1, wireless_feature$d1, main="ap1 signal to distance")
plot(wireless_feature$S2, wireless_feature$d2, main="ap2 signal to distance")

plot(wireless_feature$S3, wireless_feature$d3, main="ap3 signal to distance")
plot(wireless_feature$S4, wireless_feature$d4, main="ap4 signal to distance")

plot(wireless_feature$S5, wireless_feature$d5, main="ap5 signal to distance")
# maybe normal method for signal < 70
# modeling xlog distance when signal > 70 
plot.new()

plot(y=wireless_feature$S1, wireless_feature$d1, main="ap1 distance to signal")
plot(y=wireless_feature$S2, wireless_feature$d2, main="ap2 distance to signal")

plot(y=wireless_feature$S3, wireless_feature$d3, main="ap3 distance to signal")
plot(y=wireless_feature$S4, wireless_feature$d4, main="ap4 distance to signal")

plot(y=wireless_feature$S5, wireless_feature$d5, main="ap5 distance to signal")

From the first half of the graph, we can see for different AP, the variance spikes at different points.

Looking at the second half of the graphs.
For access point 5, the relationship between distance and signal is very weak, while
others are more stable. This may have to do with AP5 is in the center of the building.

hover_text = apply(wireless_feature,1, function(x) paste(x,collapse = "|"))
p = plot_ly(wireless, x=~x, y=~y, name = "receivers", type="scatter", 
            mode="markers", text=paste(1:254, "<br>", hover_text)) %>% 
  add_trace(x=AP$x, y=AP$y, name = "wifi post", mode="markers", text=rownames(AP))
p
kclusters = kmeans(wireless[,3:7], 5)
#kclusters$cluster

ggplot(data=wireless) +
  geom_point(aes(x=x,y=y), colour=kclusters$cluster) 
ggplot(data=wireless) +
  geom_point(aes(x=x,y=y)) +
  scale_fill_manual(kclusters$cluster)
cutoff = 68
# seems like 70 is a good cut off lets check how many points have more than 70
wireless_strong = wireless %>%
  mutate(S1 = S1 > -cutoff) %>% 
  mutate(S2 = S2 > -cutoff) %>% 
  mutate(S3 = S3 > -cutoff) %>% 
  mutate(S4 = S4 > -cutoff) %>% 
  mutate(S5 = S5 > -cutoff) 
# seems like 70 is not a good cutoff as we think
table(apply(wireless_strong[,3:7], 1, sum))

  0   1   2   3 
 19 150  80   5 
bad_locations = wireless_strong[as.numeric(apply(wireless_strong[,3:7], 1, sum)) < 2,]

plot(x=bad_locations$x, y=bad_locations$y, ylim=c(0,150), xlim=c(0,230))
points(x=AP$x,y=AP$y, col="red", cex=5)
View(data.frame(table(wireless$y)))
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3IgbGlicmFyeX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocGxvdGx5KQpgYGAKCgpgYGB7ciBpbXBvcnRfZGF0YX0KbG9hZCgid2lyZWxlc3MucmRhIikKd2lyZWxlc3NfZmVhdHVyZSA9IHdpcmVsZXNzWywzOjddICU+JQogIG11dGF0ZShkMSA9IGFwcGx5KHdpcmVsZXNzWywxOjJdLCAxLCBmdW5jdGlvbih4KXtzcXJ0KHN1bSgoeCAtIGFzLm51bWVyaWMoQVBbMSxdKSleMikpfSkpICU+JQogIG11dGF0ZShkMiA9IGFwcGx5KHdpcmVsZXNzWywxOjJdLCAxLCBmdW5jdGlvbih4KXtzcXJ0KHN1bSgoeCAtIGFzLm51bWVyaWMoQVBbMixdKSleMikpfSkpICU+JQogIG11dGF0ZShkMyA9IGFwcGx5KHdpcmVsZXNzWywxOjJdLCAxLCBmdW5jdGlvbih4KXtzcXJ0KHN1bSgoeCAtIGFzLm51bWVyaWMoQVBbMyxdKSleMikpfSkpICU+JQogIG11dGF0ZShkNCA9IGFwcGx5KHdpcmVsZXNzWywxOjJdLCAxLCBmdW5jdGlvbih4KXtzcXJ0KHN1bSgoeCAtIGFzLm51bWVyaWMoQVBbNCxdKSleMikpfSkpICU+JQogIG11dGF0ZShkNSA9IGFwcGx5KHdpcmVsZXNzWywxOjJdLCAxLCBmdW5jdGlvbih4KXtzcXJ0KHN1bSgoeCAtIGFzLm51bWVyaWMoQVBbNSxdKSleMikpfSkpCmBgYAoKYGBge3Igc2VlaW5nX3BsYWNlc30KcGxvdCh4PXdpcmVsZXNzJHgsIHk9d2lyZWxlc3MkeSkKcG9pbnRzKHg9QVAkeCx5PUFQJHksIGNvbD0icmVkIiwgY2V4PTEpCmBgYAoKCmBgYHtyIGJhZ19pbml0fQpjdXRvZmYgPSAxMDAKc2FtcGxlX3NpemUgPSA0MApudW1faXRlciA9IDEwMDAKYGBgCgoKYGBge3IgYmFnMX0Ka19zID0gbnVtZXJpYyhudW1faXRlcikKcl9zID0gbnVtZXJpYyhudW1faXRlcikKCmZvciAoaSBpbiAxOm51bV9pdGVyKSB7CiAgdGVtcF9pbmRpY2VzID0gc2FtcGxlKCgxOjI1NClbZGlzdGFuY2VzIDwgY3V0b2ZmXSwgc2FtcGxlX3NpemUpCiAgdGVtcF9tb2QgPSBsbSh3aXJlbGVzc19mZWF0dXJlW3RlbXBfaW5kaWNlcyAsMV1+ZGlzdGFuY2VzW3RlbXBfaW5kaWNlc10pCiAga19zW2ldID0gdGVtcF9tb2QkY29lZmZpY2llbnRzWzJdCiAgcl9zW2ldID0gc3VtbWFyeSh0ZW1wX21vZCkkYWRqLnIuc3F1YXJlZAp9CgptZWFuKGtfcykKbWVhbihyX3MpCmBgYAoKYGBge3IgYmFnMn0KIyB1c2luZyBiYWdnaW5nIHRvIGZpbmQgdGhlIGNvZWZmaWNpZW50cywgdXNpbmcgNDAgcG9pbnRzIAprX3MgPSBudW1lcmljKG51bV9pdGVyKQpmb3IgKGkgaW4gMTpudW1faXRlcikgewogIHRlbXBfaW5kaWNlcyA9IHNhbXBsZSgoMToyNTQpW2Rpc3RhbmNlczIgPCBjdXRvZmZdLCBzYW1wbGVfc2l6ZSkKICB0ZW1wX21vZCA9IGxtKHdpcmVsZXNzX2ZlYXR1cmVbdGVtcF9pbmRpY2VzICwyXX5kaXN0YW5jZXMyW3RlbXBfaW5kaWNlc10pCiAga19zW2ldID0gdGVtcF9tb2QkY29lZmZpY2llbnRzWzJdCn0KCm1lYW4oa19zKQpgYGAKCmBgYHtyfQojMTIvMzEvMjAxNyBkaWZmZXJlbnRpYWwgYXR0ZW50cwpkaWZmXzEyID0gKHdpcmVsZXNzX2ZlYXR1cmUkUzEgLSB3aXJlbGVzc19mZWF0dXJlJFMyKQpkaWZmXzMyID0gKHdpcmVsZXNzX2ZlYXR1cmUkUzQgLSB3aXJlbGVzc19mZWF0dXJlJFM1KQpzdW1tYXJ5KGxtKGxvZyh3aXJlbGVzc19mZWF0dXJlJGQxL3dpcmVsZXNzX2ZlYXR1cmUkZDIpfjAgKyBkaWZmXzEyKSkKCnN1bW1hcnkobG0obG9nKHdpcmVsZXNzX2ZlYXR1cmUkZDQvd2lyZWxlc3NfZmVhdHVyZSRkNSl+MCArIGRpZmZfMzIpKQoKcGxvdCgod2lyZWxlc3NfZmVhdHVyZSRTMSAtIHdpcmVsZXNzX2ZlYXR1cmUkUzIpLCBsb2cod2lyZWxlc3NfZmVhdHVyZSRkMS93aXJlbGVzc19mZWF0dXJlJGQyKSkKYGBgCgoKYGBge3J9CiMjZXhwbG9yaW5nIHVzaW5nIGRpZmZlcmVudGlhbHMgCiMjCgpzYW1wbGVfaW5kZXggPSBzYW1wbGUoMTpucm93KHdpcmVsZXNzKSwgMSkKc2FtcGxlX3BvaW50ID0gd2lyZWxlc3Nbc2FtcGxlX2luZGV4LF0Kc2FtcGxlX2RpZmYgPSBkYXRhLmZyYW1lKHQoYXBwbHkod2lyZWxlc3NbLXNhbXBsZV9pbmRleCxdLCAxLCBmdW5jdGlvbih4KSB4IC0gYXMubnVtZXJpYyh3aXJlbGVzc1tzYW1wbGVfaW5kZXgsXSkpKSkKc2FtcGxlX2RpZmZfeSA9IHNhbXBsZV9kaWZmW3NhbXBsZV9kaWZmJHkgPT0gMCxdCgptb2QgPSBsbSh4fi4teSwgZGF0YT1zYW1wbGVfZGlmZl95KQpzdW1tYXJ5KG1vZCkKYGBgCgpgYGB7cn0KYmFzaWNfeCA9IGxtKHh+Li15LCBkYXRhPXdpcmVsZXNzKQpiYXNpY195ID0gbG0oeX4uLXgsIGRhdGE9d2lyZWxlc3MpCgojc3VtbWFyeShiYXNpY194KQojc3VtbWFyeShiYXNpY195KQoKYXZnX2Vycm9yID0gbWVhbihzcXJ0KCh3aXJlbGVzcyR4IC0gYmFzaWNfeCRmaXR0ZWQudmFsdWVzKV4yICsgKHdpcmVsZXNzJHkgLSBiYXNpY195JGZpdHRlZC52YWx1ZXMpXjIpKQphdmdfZXJyb3IKYGBgCgpgYGB7ciBiYXNpY19wcmVkaWN0aW9ufQpwbG90KHdpcmVsZXNzJHgsIHdpcmVsZXNzJHkpCnBvaW50cyhiYXNpY194JGZpdHRlZC52YWx1ZXMsIGJhc2ljX3kkZml0dGVkLnZhbHVlcywgY29sPSJyZWQiKQpzZWdtZW50cyh3aXJlbGVzcyR4LCB3aXJlbGVzcyR5LCBiYXNpY194JGZpdHRlZC52YWx1ZXMsIGJhc2ljX3kkZml0dGVkLnZhbHVlcywgY29sPSJibHVlIikKYGBgCgpgYGB7ciBiYXNpY19rbm59Cm4gPSBucm93KHdpcmVsZXNzKQojdHJhaW5fcGVyY2VudCA9IDAuNgojc2FtcGxlX2luZGljZXMgPSBzYW1wbGUoMTpucm93KHdpcmVsZXNzKSwgdHJhaW5fcGVyY2VudCpuKQprbm5fcHJlZGljdGlvbnMgPSBudW1lcmljKG4pCgpwd2Rpc3RhbmNlcyA9IGFzLm1hdHJpeChkaXN0KHdpcmVsZXNzWywzOjddKSkKCmZvciAoaSBpbiAxOm4pIHsKICBrbm5fcHJlZGljdGlvbnNbaV0gPSAoMTpuKVstaV1bd2hpY2gubWluKGFzLm1hdHJpeChwZGlzdDo6cGRpc3Qod2lyZWxlc3NbLCgzOjcpXVtpLF0sIHdpcmVsZXNzWywoMzo3KV1bLWksXSkpKV0KfQpgYGAKCgpgYGB7cn0Ka25uX3ggPSB3aXJlbGVzcyR4W2tubl9wcmVkaWN0aW9uc10Ka25uX3kgPSB3aXJlbGVzcyR5W2tubl9wcmVkaWN0aW9uc10KCnBhcihtZnJvdz1jKDEsMikpCnBsb3QoZGVuc2l0eShzcXJ0KCh3aXJlbGVzcyR4IC0ga25uX3gpXjIgKyAod2lyZWxlc3MkeSAtIGtubl95KV4yKSksCiAgICAgbWFpbiA9ICJrbm4gcGVyZm9ybWFuY2UiKQojcGxvdChkZW5zaXR5KHNxcnQoKHdpcmVsZXNzJHggLSBiYXNpY194JGZpdHRlZC52YWx1ZXMpXjIgKyAod2lyZWxlc3MkeSAtIGJhc2ljX3kkZml0dGVkLnZhbHVlcyleMikpLAojICAgICBtYWluID0gInJlZ3Jlc3Npb24gcGVyZm9ybWFuY2UiKQoKa25uX2Vycm9ycyA9IHNxcnQoKHdpcmVsZXNzJHggLSBrbm5feCleMiArICh3aXJlbGVzcyR5IC0ga25uX3kpXjIpCmtubl9hdmdfZXJyb3IgPSBtZWFuKGtubl9lcnJvcnNba25uX2Vycm9ycyA8IDEwMF0pCmtubl9hdmdfZXJyb3IKYGBgCgoKYGBge3J9CnBsb3Qod2lyZWxlc3MkeCwgd2lyZWxlc3MkeSkKcG9pbnRzKGtubl94LCBrbm5feSwgY29sPSJyZWQiKQphcnJvd3Mod2lyZWxlc3MkeCwgd2lyZWxlc3MkeSwga25uX3gsIGtubl95LCBjb2w9ImJsdWUiLCBsZW5ndGggPSAwLjEpCmBgYAoKYGBge3Iga25uX2Vycm9yX2FuYWx5c2lzfQprbm5fYmFkX2xvYyA9IHdpcmVsZXNzW2tubl9lcnJvcnMgPiAyMCxdICU+JQogIG11dGF0ZShlcnJvciA9IGtubl9lcnJvcnNba25uX2Vycm9ycyA+IDIwXSkgJT4lCiAgbXV0YXRlKGluZGV4ID0gKDE6MjU0KVtrbm5fZXJyb3JzID4gMjBdKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKGVycm9yKSkKYGBgCgpgYGB7cn0KZXJyb3JfYmFyID0gMTUKcGxvdCh3aXJlbGVzcyR4LCB3aXJlbGVzcyR5KQpwb2ludHMoa25uX3hba25uX2Vycm9ycyA+IGVycm9yX2Jhcl0sIGtubl95W2tubl9lcnJvcnMgPiBlcnJvcl9iYXJdLCBjb2w9InJlZCIpCmFycm93cyh3aXJlbGVzcyR4W2tubl9lcnJvcnMgPiBlcnJvcl9iYXJdLCB3aXJlbGVzcyR5W2tubl9lcnJvcnMgPiBlcnJvcl9iYXJdLCBrbm5feFtrbm5fZXJyb3JzID4gZXJyb3JfYmFyXSwga25uX3lba25uX2Vycm9ycyA+IGVycm9yX2Jhcl0sIGNvbD0iYmx1ZSIsIGxlbmd0aCA9IDAuMSkKYGBgCgpgYGB7cn0KcGFyKG1mcm93PWMoMSwyKSkKaGlzdChrbm5fZXJyb3JzKQpwbG90KGRlbnNpdHkoa25uX2Vycm9ycykpCmBgYAoKCmBgYHtyfQpwYXIobWZyb3c9YygxLDIpKQpoaXN0KHdpcmVsZXNzJHhba25uX2Vycm9ycyA+IGVycm9yX2Jhcl0pCnBsb3QoZGVuc2l0eSh3aXJlbGVzcyR4W2tubl9lcnJvcnMgPiBlcnJvcl9iYXJdKSkKYGBgCgoKYGBge3J9CiN0cnlpbmcgbmVhcmVzdCBuZWlnaHRib3IgKyB0cmlndWxhdGlvbgpwb2ludF9pbmRleCA9IDExMwpwb2ludCA9IHdpcmVsZXNzW3BvaW50X2luZGV4LDM6N10KIyBkaWZmZXJlbmNlIGJhc2VkIG9uIHNpZ25hbCBzdHJlbmd0aCBmcm9tIGRpZmZlcmVudCBhY2Nlc3MgcG9pbnQKcG9pbnRfZCA9IHBkaXN0OjpwZGlzdChwb2ludCwgd2lyZWxlc3NbLXBvaW50X2luZGV4LDM6N10pQGRpc3QKIyByZWFsIGRpc3RhbmNlIGJhc2VkIG9uIGNvb3JkaW5hdGVzCnBvaW50X3JkID0gcGRpc3Q6OnBkaXN0KHdpcmVsZXNzW3BvaW50X2luZGV4LDE6Ml0sIHdpcmVsZXNzWy1wb2ludF9pbmRleCwxOjJdKUBkaXN0CgoKc2lnbmFsX25laWdoYm9yID0gKDE6MjU0KVstcG9pbnRfaW5kZXhdW29yZGVyKHBvaW50X2QpWzE6M11dCnJlYWxfbmVpZ2hib3JzID0gKDE6MjU0KVstcG9pbnRfaW5kZXhdW29yZGVyKHBvaW50X3JkKVsxOjNdXQpgYGAKCmBgYHtyIHZpc3VhbGl6aW5nfQpob3Zlcl90ZXh0ID0gYXBwbHkod2lyZWxlc3NfZmVhdHVyZVssMTo1XSwxLCBmdW5jdGlvbih4KSBwYXN0ZSh4LGNvbGxhcHNlID0gInwiKSkKcCA9IHBsb3RfbHkod2lyZWxlc3MsIHg9fngsIHk9fnksIG5hbWUgPSAicmVjZWl2ZXJzIiwgdHlwZT0ic2NhdHRlciIsIAogICAgICAgICAgICBtb2RlPSJtYXJrZXJzIiwgdGV4dD1wYXN0ZSgxOjI1NCwgIjxicj4iLCBob3Zlcl90ZXh0KSkgJT4lIAogIGFkZF90cmFjZSh4PUFQJHgsIHk9QVAkeSwgbmFtZSA9ICJ3aWZpIHBvc3QiLCBtb2RlPSJtYXJrZXJzIiwgdGV4dD1yb3duYW1lcyhBUCkpICU+JQogIGFkZF90cmFjZSh4PXdpcmVsZXNzJHhbc2lnbmFsX25laWdoYm9yXSwgeT13aXJlbGVzcyR5W3NpZ25hbF9uZWlnaGJvcl0sIAogICAgICAgICAgICBuYW1lID0gIm5laWdoYm9ycyIsIG1vZGU9Im1hcmtlcnMiLCAKICAgICAgICAgICAgdGV4dD1wYXN0ZShzaWduYWxfbmVpZ2hib3IsICI8YnI+IiwgaG92ZXJfdGV4dFtzaWduYWxfbmVpZ2hib3JdKSkgJT4lCiAgYWRkX3RyYWNlKHg9d2lyZWxlc3MkeFtyZWFsX25laWdoYm9yc10sIHk9d2lyZWxlc3MkeVtyZWFsX25laWdoYm9yc10sIAogICAgICAgICAgICBuYW1lID0gIm5laWdoYm9ycyIsIG1vZGU9Im1hcmtlcnMiLCAKICAgICAgICAgICAgdGV4dD1wYXN0ZShyZWFsX25laWdoYm9ycywgIjxicj4iLCBob3Zlcl90ZXh0W3JlYWxfbmVpZ2hib3JzXSkpICU+JQogIGFkZF90cmFjZSh4PXdpcmVsZXNzJHhbcG9pbnRfaW5kZXhdLCB5PXdpcmVsZXNzJHlbcG9pbnRfaW5kZXhdLCAKICAgICAgICAgICAgbmFtZSA9ICJuZWlnaGJvcnMiLCBtb2RlPSJtYXJrZXJzIiwgCiAgICAgICAgICAgIHRleHQ9cGFzdGUocG9pbnRfaW5kZXgsICI8YnI+IiwgaG92ZXJfdGV4dFtwb2ludF9pbmRleF0pKQpwCmBgYAoKYGBge3Igc2luZ2xlX3BvaW50X2ZhaWx1cmV9CiN3aXJlbGVzc1tjKHBvaW50X2luZGV4LCBzaWduYWxfbmVpZ2hib3IpLF0KCnBvaW50X25laWdoYm9ycyA9IGRhdGEuZnJhbWUodChhcHBseSh3aXJlbGVzc1tjKHNpZ25hbF9uZWlnaGJvciwgcmVhbF9uZWlnaGJvcnMpLF0sIDEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgYXMubnVtZXJpYyh4LXdpcmVsZXNzW3BvaW50X2luZGV4LCBdKSkpKSAlPiUKICBkcGx5cjo6cmVuYW1lKGR4PVgxLGR5PVgyLGRTMT1YMyxkUzI9WDQsZFMzPVg1LGRTND1YNixkUzU9WDcpICU+JQogIGRwbHlyOjptdXRhdGUocmVhbF9kaXN0YW5jZT1jKHBvaW50X3JkW29yZGVyKHBvaW50X2QpWzE6M11dLCBwb2ludF9yZFtvcmRlcihwb2ludF9yZClbMTozXV0pKSAlPiUgCiAgZHBseXI6Om11dGF0ZShzaWduYWxfZGlzdGFuY2U9Yyhwb2ludF9kW29yZGVyKHBvaW50X2QpWzE6M11dLCBwb2ludF9kW29yZGVyKHBvaW50X3JkKVsxOjNdXSkpICU+JSAKICBkcGx5cjo6bXV0YXRlKHJlbGF0aW9uc2hpcD1jKHJlcCgic2lnbmFsX25laWdoYm9yIiwzKSxyZXAoInJlYWxfbmVpZ2hib3IiLCAzKSkpICU+JQogIHJldigpCgpwbG90LnRhYmxlKHBvaW50X25laWdoYm9ycykKYGBgCgoKYGBge3J9CnBvaW50X2luZGV4ID0gMTEzCnBvaW50ID0gd2lyZWxlc3NbcG9pbnRfaW5kZXgsMzo3XQpwb2ludF9kID0gcGRpc3Q6OnBkaXN0KHBvaW50LCB3aXJlbGVzc1stcG9pbnRfaW5kZXgsKDM6NyldKUBkaXN0CnNpZ25hbF9uZWlnaGJvciA9ICgxOjI1NClbLXBvaW50X2luZGV4XVtvcmRlcihwb2ludF9kKVsxOjNdXQoKCm5laWdoYm9ycyA9IG51bWVyaWMoNSkKZm9yIChpIGluIDE6NSkgewogIG5laWdoYm9yc1tpXSA9ICgxOm4pWy1wb2ludF9pbmRleF1bd2hpY2gubWluKGFzLm1hdHJpeChwZGlzdDo6cGRpc3Qod2lyZWxlc3NbLCgzOjcpWy1pXV1bcG9pbnRfaW5kZXgsXSwgd2lyZWxlc3NbLCgzOjcpWy1pXV1bLXBvaW50X2luZGV4LF0pKSldCn0KCm5laWdoYm9ycwpkaXN0KHdpcmVsZXNzW25laWdoYm9ycywxOjJdKQpgYGAKNQpgYGB7cn0KcG9pbnQKYGBgCgoKYGBge3J9CmhvdmVyX3RleHQgPSBhcHBseSh3aXJlbGVzc19mZWF0dXJlWywxOjVdLDEsIGZ1bmN0aW9uKHgpIHBhc3RlKHgsY29sbGFwc2UgPSAifCIpKQpwID0gcGxvdF9seSh3aXJlbGVzcywgeD1+eCwgeT1+eSwgbmFtZSA9ICJyZWNlaXZlcnMiLCB0eXBlPSJzY2F0dGVyIiwgCiAgICAgICAgICAgIG1vZGU9Im1hcmtlcnMiLCB0ZXh0PXBhc3RlKDE6MjU0LCAiPGJyPiIsIGhvdmVyX3RleHQpKSAlPiUgCiAgYWRkX3RyYWNlKHg9QVAkeCwgeT1BUCR5LCBuYW1lID0gIndpZmkgcG9zdCIsIG1vZGU9Im1hcmtlcnMiLCB0ZXh0PXJvd25hbWVzKEFQKSkgJT4lCiAgYWRkX3RyYWNlKHg9d2lyZWxlc3MkeFtuZWlnaGJvcnNdLCB5PXdpcmVsZXNzJHlbbmVpZ2hib3JzXSwgCiAgICAgICAgICAgIG5hbWUgPSAibmVpZ2hib3JzIiwgbW9kZT0ibWFya2VycyIsIAogICAgICAgICAgICB0ZXh0PXBhc3RlKG5laWdoYm9ycywgIjxicj4iLCBob3Zlcl90ZXh0W25laWdoYm9yc10pKSAjJT4lCiAgI2FkZF90cmFjZSh4PXdpcmVsZXNzJHhbcG9pbnRfaW5kZXhdLCB5PXdpcmVsZXNzJHlbcG9pbnRfaW5kZXhdLCAKICAjICAgICAgICAgIG5hbWUgPSAicG9pbnQiLCBtb2RlPSJtYXJrZXJzIiwgCiAgIyAgICAgICAgICB0ZXh0PXBhc3RlKHBvaW50X2luZGV4LCAiPGJyPiIsIGhvdmVyX3RleHRbcG9pbnRfaW5kZXhdKSkKICAKcApgYGAKCgoKYGBge3J9Cmtubl9wcmVkaWN0aW9uc1szXQpgYGAKCmBgYHtyfQpuZWlnaGJvcnMgPSBudW1lcmljKDUpCmZvciAoaSBpbiAxOjUpIHsKICBuZWlnaGJvcnNbaV0gPSAoMTpuKVstcG9pbnRfaW5kZXhdW3doaWNoLm1pbihhcy5tYXRyaXgocGRpc3Q6OnBkaXN0KDEvd2lyZWxlc3NbLCgzOjcpWy1pXV1bcG9pbnRfaW5kZXgsXSwgMS93aXJlbGVzc1ssKDM6NylbLWldXVstcG9pbnRfaW5kZXgsXSkpKV0KfQpuZWlnaGJvcnMKYGBgCgoKYWZ0ZXIgYW5hbHl6aW5nIHRoZSBlcnJvciwgSSBmaW5kIHRoYXQgcG9pbnQgMjQzJ3Mgc2lnbmFsIGZvciBBUDMgaXMgY29tcGxldGVseSBiYWQKY29tcGFyaW5nIHRvIGl0cyBuZWlnaGJvcnMsIGxldHMgY2hlY2sgb3RoZXIgYWNjZXNzIHBvaW50cy4KClNvbWUgcG9pbnRzLCB0aGV5IG9ubHkgZiogdXAgb24gc2lnbmFsIGZyb20gYW4gYWNjZXNzIHBvaW50LiAKCmBgYHtyfQpzZXQuc2VlZCgxMjM0NSkKc2FtcGxlX2luZGljZXMgPSBzYW1wbGUoMToyNTQsIDUwKQpwbG90KHdpcmVsZXNzJHhbc2FtcGxlX2luZGljZXNdLCB3aXJlbGVzcyR5W3NhbXBsZV9pbmRpY2VzXSwgeWxpbT1jKDAsIDE0NSksIHhsaW09YygxMCwgMjM1KSkKcG9pbnRzKGtubl94W3NhbXBsZV9pbmRpY2VzXSwga25uX3lbc2FtcGxlX2luZGljZXNdLCBjb2w9InllbGxvdyIpCnBvaW50cygoa25uX3gtZGZfeClbc2FtcGxlX2luZGljZXNdLCAoa25uX3ktZGZfeSlbc2FtcGxlX2luZGljZXNdLCBjb2w9InJlZCIpCgpzZWdtZW50cyh3aXJlbGVzcyR4W3NhbXBsZV9pbmRpY2VzXSwgd2lyZWxlc3MkeVtzYW1wbGVfaW5kaWNlc10sIGtubl94W3NhbXBsZV9pbmRpY2VzXSwga25uX3lbc2FtcGxlX2luZGljZXNdLCBjb2w9ImJsdWUiKQpzZWdtZW50cyhrbm5feFtzYW1wbGVfaW5kaWNlc10sIGtubl95W3NhbXBsZV9pbmRpY2VzXSwgKGtubl94LWRmX3gpW3NhbXBsZV9pbmRpY2VzXSwgKGtubl95LWRmX3kpW3NhbXBsZV9pbmRpY2VzXSwgY29sPSJncmVlbiIpCiNzZWdtZW50cyh3aXJlbGVzcyR4LCB3aXJlbGVzcyR5LCBrbm5feCtkZl94LCBrbm5feSsgZGZfeSwgY29sPSJncmVlbiIpCmBgYAoKCmBgYHtyIHNpZ25hbF9hbmRfZGlzdGFuY2VzfQpwYXIobWZyb3c9YygxLDIpKQpwbG90KHdpcmVsZXNzX2ZlYXR1cmUkUzEsIHdpcmVsZXNzX2ZlYXR1cmUkZDEsIG1haW49ImFwMSBzaWduYWwgdG8gZGlzdGFuY2UiKQpwbG90KHdpcmVsZXNzX2ZlYXR1cmUkUzIsIHdpcmVsZXNzX2ZlYXR1cmUkZDIsIG1haW49ImFwMiBzaWduYWwgdG8gZGlzdGFuY2UiKQpwbG90KHdpcmVsZXNzX2ZlYXR1cmUkUzMsIHdpcmVsZXNzX2ZlYXR1cmUkZDMsIG1haW49ImFwMyBzaWduYWwgdG8gZGlzdGFuY2UiKQpwbG90KHdpcmVsZXNzX2ZlYXR1cmUkUzQsIHdpcmVsZXNzX2ZlYXR1cmUkZDQsIG1haW49ImFwNCBzaWduYWwgdG8gZGlzdGFuY2UiKQpwbG90KHdpcmVsZXNzX2ZlYXR1cmUkUzUsIHdpcmVsZXNzX2ZlYXR1cmUkZDUsIG1haW49ImFwNSBzaWduYWwgdG8gZGlzdGFuY2UiKQojIG1heWJlIG5vcm1hbCBtZXRob2QgZm9yIHNpZ25hbCA8IDcwCiMgbW9kZWxpbmcgeGxvZyBkaXN0YW5jZSB3aGVuIHNpZ25hbCA+IDcwIApwbG90Lm5ldygpCgpwbG90KHk9d2lyZWxlc3NfZmVhdHVyZSRTMSwgd2lyZWxlc3NfZmVhdHVyZSRkMSwgbWFpbj0iYXAxIGRpc3RhbmNlIHRvIHNpZ25hbCIpCnBsb3QoeT13aXJlbGVzc19mZWF0dXJlJFMyLCB3aXJlbGVzc19mZWF0dXJlJGQyLCBtYWluPSJhcDIgZGlzdGFuY2UgdG8gc2lnbmFsIikKcGxvdCh5PXdpcmVsZXNzX2ZlYXR1cmUkUzMsIHdpcmVsZXNzX2ZlYXR1cmUkZDMsIG1haW49ImFwMyBkaXN0YW5jZSB0byBzaWduYWwiKQpwbG90KHk9d2lyZWxlc3NfZmVhdHVyZSRTNCwgd2lyZWxlc3NfZmVhdHVyZSRkNCwgbWFpbj0iYXA0IGRpc3RhbmNlIHRvIHNpZ25hbCIpCnBsb3QoeT13aXJlbGVzc19mZWF0dXJlJFM1LCB3aXJlbGVzc19mZWF0dXJlJGQ1LCBtYWluPSJhcDUgZGlzdGFuY2UgdG8gc2lnbmFsIikKCmBgYAoKRnJvbSB0aGUgZmlyc3QgaGFsZiBvZiB0aGUgZ3JhcGgsIHdlIGNhbiBzZWUgZm9yIGRpZmZlcmVudCBBUCwKdGhlIHZhcmlhbmNlIHNwaWtlcyBhdCBkaWZmZXJlbnQgcG9pbnRzLiAKCkxvb2tpbmcgYXQgdGhlIHNlY29uZCBoYWxmIG9mIHRoZSBncmFwaHMuICAKRm9yIGFjY2VzcyBwb2ludCA1LCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gZGlzdGFuY2UgYW5kIHNpZ25hbCBpcyB2ZXJ5IHdlYWssIHdoaWxlICAKb3RoZXJzIGFyZSBtb3JlIHN0YWJsZS4gVGhpcyBtYXkgaGF2ZSB0byBkbyB3aXRoIEFQNSBpcyBpbiB0aGUgY2VudGVyIG9mIHRoZSBidWlsZGluZy4gCgoKYGBge3IgcGxvdGx5fQpob3Zlcl90ZXh0ID0gYXBwbHkod2lyZWxlc3NfZmVhdHVyZSwxLCBmdW5jdGlvbih4KSBwYXN0ZSh4LGNvbGxhcHNlID0gInwiKSkKcCA9IHBsb3RfbHkod2lyZWxlc3MsIHg9fngsIHk9fnksIG5hbWUgPSAicmVjZWl2ZXJzIiwgdHlwZT0ic2NhdHRlciIsIAogICAgICAgICAgICBtb2RlPSJtYXJrZXJzIiwgdGV4dD1wYXN0ZSgxOjI1NCwgIjxicj4iLCBob3Zlcl90ZXh0KSkgJT4lIAogIGFkZF90cmFjZSh4PUFQJHgsIHk9QVAkeSwgbmFtZSA9ICJ3aWZpIHBvc3QiLCBtb2RlPSJtYXJrZXJzIiwgdGV4dD1yb3duYW1lcyhBUCkpCgpwCmBgYAoKCmBgYHtyfQprY2x1c3RlcnMgPSBrbWVhbnMod2lyZWxlc3NbLDM6N10sIDUpCiNrY2x1c3RlcnMkY2x1c3RlcgoKZ2dwbG90KGRhdGE9d2lyZWxlc3MpICsKICBnZW9tX3BvaW50KGFlcyh4PXgseT15KSwgY29sb3VyPWtjbHVzdGVycyRjbHVzdGVyKSAKYGBgCgoKYGBge3J9CmdncGxvdChkYXRhPXdpcmVsZXNzKSArCiAgZ2VvbV9wb2ludChhZXMoeD14LHk9eSkpICsKICBzY2FsZV9maWxsX21hbnVhbChrY2x1c3RlcnMkY2x1c3RlcikKCmBgYAoKCmBgYHtyfQpjdXRvZmYgPSA2OAojIHNlZW1zIGxpa2UgNzAgaXMgYSBnb29kIGN1dCBvZmYgbGV0cyBjaGVjayBob3cgbWFueSBwb2ludHMgaGF2ZSBtb3JlIHRoYW4gNzAKd2lyZWxlc3Nfc3Ryb25nID0gd2lyZWxlc3MgJT4lCiAgbXV0YXRlKFMxID0gUzEgPiAtY3V0b2ZmKSAlPiUgCiAgbXV0YXRlKFMyID0gUzIgPiAtY3V0b2ZmKSAlPiUgCiAgbXV0YXRlKFMzID0gUzMgPiAtY3V0b2ZmKSAlPiUgCiAgbXV0YXRlKFM0ID0gUzQgPiAtY3V0b2ZmKSAlPiUgCiAgbXV0YXRlKFM1ID0gUzUgPiAtY3V0b2ZmKSAKCiMgc2VlbXMgbGlrZSA3MCBpcyBub3QgYSBnb29kIGN1dG9mZiBhcyB3ZSB0aGluawp0YWJsZShhcHBseSh3aXJlbGVzc19zdHJvbmdbLDM6N10sIDEsIHN1bSkpCmBgYAoKYGBge3J9CmJhZF9sb2NhdGlvbnMgPSB3aXJlbGVzc19zdHJvbmdbYXMubnVtZXJpYyhhcHBseSh3aXJlbGVzc19zdHJvbmdbLDM6N10sIDEsIHN1bSkpIDwgMixdCgpwbG90KHg9YmFkX2xvY2F0aW9ucyR4LCB5PWJhZF9sb2NhdGlvbnMkeSwgeWxpbT1jKDAsMTUwKSwgeGxpbT1jKDAsMjMwKSkKcG9pbnRzKHg9QVAkeCx5PUFQJHksIGNvbD0icmVkIiwgY2V4PTUpCmBgYAoKCmBgYHtyfQpWaWV3KGRhdGEuZnJhbWUodGFibGUod2lyZWxlc3MkeSkpKQoKYGBgCgo=